public class 解释器 {

    public static final int 默认操作数栈大小 = 100;
    public static final int 默认调用栈大小 = 1000;

    int 指令指针;   // 指令指针寄存器（IP）
    byte[] 机器码;
    int 机器码尺寸;
    Object[] 全局变量;

    protected Object[] 常量池;

    Object[] 操作数 = new Object[默认操作数栈大小];
    int 栈指针 = -1; // sp stack pointer register 栈指针寄存器

    栈帧[] 调用栈 = new 栈帧[默认调用栈大小];
    int 帧指针 = -1; // fp frame pointer register

    函数符号 入口函数;

    boolean 输出日志;

    private 反汇编器 反汇编器;

    public StringBuilder 输出缓存 = new StringBuilder();

    public 解释器(boolean 输出日志) {
        this.输出日志 = 输出日志;
    }

    public void 加载(byte[] 机器码, int 机器码尺寸, Object[] 常量池, 函数符号 入口函数, Object[] 全局变量) {
        this.机器码 = 机器码;
        this.机器码尺寸 = 机器码尺寸;
        this.常量池 = 常量池;
        this.入口函数 = 入口函数;
        this.全局变量 = 全局变量;

        反汇编器 = new 反汇编器(机器码, 机器码尺寸, 常量池);
    }

    public void 执行() {
        if (入口函数 == null) {
            入口函数 = new 函数符号("main", 0, 0, 0);
        }

        var 帧 = new 栈帧(入口函数, -1);
        帧指针++;
        调用栈[帧指针] = 帧;
        指令指针 = 入口函数.地址;
        cpu();
    }

    protected void cpu() {
        Object v; // 存一些可重用的局部变量
        int a, b; // 整数型寄存器 a,b
        float e, f; // 浮点型寄存器 e,f
        int 地址 = 0; // 地址寄存器
        short 指令 = 机器码[指令指针];

        while (指令 != 字节码定义.指令_HALT && 指令指针 < 机器码尺寸) {
            if (输出日志) {
                跟踪显示();
            }

            指令指针++; // 跳转到下一条指令或操作数的第一个字节
            switch (指令) {
                case 字节码定义.指令_IADD:
                    a = (int) 操作数[栈指针 - 1];
                    b = (int) 操作数[栈指针];
                    栈指针 -= 2; // 把两个操作数出栈
                    栈指针++; // 申请一个空间去存结果
                    操作数[栈指针] = a + b; // 把结果入栈
                    break;

                case 字节码定义.指令_ISUB:
                    a = (int) 操作数[栈指针 - 1];
                    b = (int) 操作数[栈指针];
                    栈指针 -= 2;
                    栈指针++;
                    操作数[栈指针] = a - b;
                    break;

                case 字节码定义.指令_IMUL:
                    a = (int) 操作数[栈指针 - 1];
                    b = (int) 操作数[栈指针];
                    栈指针 -= 2;
                    栈指针++;
                    操作数[栈指针] = a * b;
                    break;

                case 字节码定义.指令_ILT:
                    a = (int) 操作数[栈指针 - 1];
                    b = (int) 操作数[栈指针];
                    栈指针 -= 2;
                    栈指针++;
                    操作数[栈指针] = a < b;
                    break;

                case 字节码定义.指令_IEQ:
                    a = (int) 操作数[栈指针 - 1];
                    b = (int) 操作数[栈指针];
                    栈指针 -= 2;
                    栈指针++;
                    操作数[栈指针] = a == b;
                    break;

                case 字节码定义.指令_FADD:
                    e = (float) 操作数[栈指针 - 1];
                    f = (float) 操作数[栈指针];
                    栈指针 -= 2;
                    栈指针++;
                    操作数[栈指针] = e + f;
                    break;

                case 字节码定义.指令_FSUB:
                    e = (float) 操作数[栈指针 - 1];
                    f = (float) 操作数[栈指针];
                    栈指针 -= 2;
                    栈指针++;
                    操作数[栈指针] = e - f;
                    break;

                case 字节码定义.指令_FMUL:
                    e = (float) 操作数[栈指针 - 1];
                    f = (float) 操作数[栈指针];
                    栈指针 -= 2;
                    栈指针++;
                    操作数[栈指针] = e * f;
                    break;

                case 字节码定义.指令_FLT:
                    e = (float) 操作数[栈指针 - 1];
                    f = (float) 操作数[栈指针];
                    栈指针 -= 2;
                    栈指针++;
                    操作数[栈指针] = e < f;
                    break;

                case 字节码定义.指令_FEQ:
                    e = (float) 操作数[栈指针 - 1];
                    f = (float) 操作数[栈指针];
                    栈指针 -= 2;
                    栈指针++;
                    操作数[栈指针] = e == f;
                    break;

                case 字节码定义.指令_ITOF:
                    a = (int) 操作数[栈指针];
                    栈指针--;
                    栈指针++;
                    操作数[栈指针] = (float) a;
                    break;

                case 字节码定义.指令_CALL:
                    var 函数常量池索引 = 取整数型操作数();
                    调用函数(函数常量池索引);
                    break;

                case 字节码定义.指令_RET:
                    var 帧 = 调用栈[帧指针];
                    帧指针--;
                    指令指针 = 帧.返回地址;
                    break;

                case 字节码定义.指令_BR: // jmp addr
                    指令指针 = 取整数型操作数();
                    break;

                case 字节码定义.指令_BRT:
                    地址 = 取整数型操作数();
                    if (操作数[栈指针].equals(true)) {
                        指令指针 = 地址;
                    }

                    栈指针--;
                    break;

                case 字节码定义.指令_BRF:
                    地址 = 取整数型操作数();
                    if (操作数[栈指针].equals(false)) {
                        指令指针 = 地址;
                    }

                    栈指针--;
                    break;

                case 字节码定义.指令_CCONST:
                    栈指针++;
                    操作数[栈指针] = (char) 取整数型操作数();
                    break;

                case 字节码定义.指令_ICONST:
                    栈指针++;
                    操作数[栈指针] = 取整数型操作数();
                    break;

                case 字节码定义.指令_FCONST:
                case 字节码定义.指令_SCONST:
                    栈指针++;
                    操作数[栈指针] = 常量池[取整数型操作数()];
                    break;

                case 字节码定义.指令_LOAD:
                    地址 = 取整数型操作数();
                    栈指针++;
                    操作数[栈指针] = 调用栈[帧指针].局部变量[地址];
                    break;

                case 字节码定义.指令_GLOAD:
                    地址 = 取整数型操作数();
                    栈指针++;
                    操作数[栈指针] = 全局变量[地址];
                    break;

                case 字节码定义.指令_FLOAD: // fload 字段偏移值(int)
                {
                    var 结构体 = (结构体空间) 操作数[栈指针];
                    栈指针--;

                    var 字段偏移值 = 取整数型操作数();
                    栈指针++;
                    操作数[栈指针] = 结构体.字段[字段偏移值];
                }
                break;

                case 字节码定义.指令_STORE:
                    地址 = 取整数型操作数();
                    调用栈[帧指针].局部变量[地址] = 操作数[栈指针];
                    栈指针--;
                    break;

                case 字节码定义.指令_GSTORE:
                    地址 = 取整数型操作数();
                    全局变量[地址] = 操作数[栈指针];
                    栈指针--;
                    break;

                case 字节码定义.指令_FSTORE: {
                    var 结构体 = (结构体空间) 操作数[栈指针];
                    栈指针--;

                    var 字段偏移值 = 取整数型操作数();
                    结构体.字段[字段偏移值] = 操作数[栈指针];
                    栈指针--;
                    break;
                }

                case 字节码定义.指令_PRINT:
                    v = 操作数[栈指针];
                    栈指针--;
                    System.out.print(Tools.标蓝(v));
                    输出缓存.append(v);
                    break;

                case 字节码定义.指令_STRUCT: {
                    var 字段个数 = 取整数型操作数();
                    var 结构体 = new 结构体空间(字段个数);
                    栈指针++;
                    操作数[栈指针] = 结构体;
                    break;
                }

                case 字节码定义.指令_NULL:
                    栈指针++;
                    操作数[栈指针] = null;
                    break;

                case 字节码定义.指令_POP:
                    栈指针--;
                    break;

                default:
                    throw new Error("无效指令: " + 指令 + ", 位置：" + 指令指针);
            }

            指令 = 机器码[指令指针];
        }

    }

    private int 取整数型操作数() {
        int 操作数 = 字节码汇编器.取整数(机器码, 指令指针);
        指令指针 += 4;
        return 操作数;
    }

    private void 调用函数(int 函数常量池索引) {
        var 符号 = (函数符号) 常量池[函数常量池索引];
        var 帧 = new 栈帧(符号, 指令指针);
        帧指针++;
        调用栈[帧指针] = 帧;

        // 从栈里把函数调用参数取出来
        for (int 索引 = 符号.参数个数 - 1; 索引 >= 0; 索引--) {
            帧.局部变量[索引] = 操作数[栈指针];
            栈指针--;
        }

        指令指针 = 符号.地址;
    }


    private void 跟踪显示() {
        反汇编器.反汇编指令(指令指针);
        System.out.print("\t 栈=[");
        for (int i = 0; i <= 栈指针; i++) {
            System.out.print(" " + 操作数[i]);
        }

        System.out.print(" ]");
        
        if (帧指针 >= 0) {
            System.out.print(", 调用栈=[");

            for (int i = 0; i < 帧指针; i++) {
                System.out.print(" " + 调用栈[i].符号.名称);
            }

            System.out.print(" ]");
        }

        System.out.println();
    }

}
